#include "config.h"

#include <dirent.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <stdarg.h>
#include <signal.h>

#define DBG_SUBSYS S_LIBTASK

#include "sysy_lib.h"
#include "cache.h"
#include "get_version.h"
#include "vnode.h"
#include "cluster.h"
#include "volume.h"
#include "lichstor.h"
#include "stor_root.h"
#include "lich_md.h"
#include "md_map.h"
#include "configure.h"
#include "job_dock.h"
#include "chunk.h"
#include "ynet_rpc.h"
#include "net_global.h"
#include "ylog.h"
#include "dbg.h"
#include "lichstor.h"
#include "timer.h"
#include "rmsnap_bh.h"
#include "system.h"
#include "bh_task.h"

static int bh_task_root(const char *pool, const char *root, fileid_t *rootid)
{
        int ret;

        ret = stor_root(pool, root, rootid);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        return 0;
err_ret:
        return ret;
}

int bh_task_create(const char *pool, const char *root, const fileid_t *fileid)
{
        int ret;
        fileid_t rootid;
        char name[MAX_NAME_LEN];

        ret = bh_task_root(pool, root, &rootid);
        if (ret)
                GOTO(err_ret, ret);

        snprintf(name, MAX_NAME_LEN, CHKID_FORMAT, CHKID_ARG(fileid));
        // 如上一任务没完成，会返回EEXIST
        ret = stor_mkvol(&rootid, name, NULL, NULL);
        if (ret)
                GOTO(err_ret, ret);

        DINFO("%s/%s created\n", pool, root);
        return 0;
err_ret:
        return ret;
}

int bh_task_remove(const char *pool, const char *root, const fileid_t *fileid)
{
        int ret;
        fileid_t rootid;
        char name[MAX_NAME_LEN];

        ret = bh_task_root(pool, root, &rootid);
        if (ret)
                GOTO(err_ret, ret);

        snprintf(name, MAX_NAME_LEN, CHKID_FORMAT, CHKID_ARG(fileid));
        ret = stor_rmvol(&rootid, name, 0);
        if (ret)
                GOTO(err_ret, ret);

        DBUG("%s%s removed\n", pool, root);
        return 0;
err_ret:
        return ret;
}

STATIC int __bh_task_pool_worker(void *pool, void *_arg)
{
        int ret, delen, success = 0, retry = 0;
        uint64_t offset = 0, offset2 = 0;
        char buf[BIG_BUF_LEN], uuid[MAX_NAME_LEN] = {};
        struct dirent *de;
        fileid_t rootid;
        bh_task_t *task = _arg;

        (void) _arg;

        DBUG("pool %s task %s\n", (const char *)pool, task->name);

retry0:
        ret = stor_root(pool, task->root, &rootid);
        if (unlikely(ret)) {
                if (ret == EAGAIN) {
                        USLEEP_RETRY(err_ret, ret, retry0, retry, 3, (1000 * 1000));
                } else
                        GOTO(err_ret, ret);
        }

        get_uuid(uuid);
        ret = md_listpool_open(&rootid, uuid);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        while(1) {
retry:
                success = 0;
                delen = BIG_BUF_LEN;
                memset(buf, 0, delen);
                ret = md_listpool(&rootid, uuid, offset, buf, &delen);
                if (unlikely(ret)) {
                        USLEEP_RETRY(err_close, ret, retry, retry, 10, (1000 * 1000));
                }

                DBUG("task %s delen %d\n", task->name, delen);

                if (delen == 0)
                        break;

                offset2 = 0;
                dir_for_each(buf, delen, de, offset2) {
                        DBUG("%s rootid %s name %s\n", task->name, id2str(&rootid), de->d_name);

                        if (strlen(de->d_name) == 0)
                                goto out;
                        else if (delen - offset2 < sizeof(*de) + MAX_NAME_LEN)
                                break;

                        offset += de->d_reclen;

                        if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..") ||
                                        de->d_name[0] == '.') {
                                continue;
                        }

                        // DINFO("%s rootid %s name %s\n", task->name, id2str(&rootid), de->d_name);

                        // ret = volume_rollback_bh(&rootid, de->d_name);
                        ret = task->func(&rootid, de->d_name);
                        if (unlikely(ret)) {
                                if (ret == ENOENT) {
                                        DWARN("%s %s not exists, maybe deleted.\n", task->name, de->d_name);
                                        ret = stor_rmvol(&rootid, de->d_name, 0);
                                        if (unlikely(ret)) {
                                                DERROR("%s remove %s fail ret:%d\n", task->name, de->d_name, ret);
                                                continue;
                                        }
                                } else {
                                        DERROR("%s %s fail ret:%d\n", task->name, de->d_name, ret);
                                        continue;
                                }
                        }

                        success++;
                }

        }
out:
        md_listpool_close(&rootid, uuid);

        return 0;
err_close:
        md_listpool_close(&rootid, uuid);
err_ret:
        DWARN("%s file error ret (%d) %s\n", task->name, ret, strerror(ret));
        SWARN(0, "%s, %s file error ret (%d) %s\n", M_DATA_VOLUME_WARN, task->name, ret, strerror(ret));

        //TODO
        //存储池的遍历过程中发生错误，但不能返回错误　这样会影响下一个存储池
        return 0;
        //return ret;
}

STATIC int __bh_task_worker(void *_args)
{
        int ret;
        bh_task_t *task = _args;

        if (task->inited == 0) {
                DINFO("task %s stoped\n", task->name);
                return 0;
        }
        
        ret = system_pool_iterator(__bh_task_pool_worker, _args);
        if (unlikely(ret)) {
                DERROR("etcd list storage failed, ret: %d\n", ret);
        }

        ret = timer1_settime(&task->worker_handler, task->interval);
        if (unlikely(ret)) {
                DERROR("settime error ret %d\n", ret);
                YASSERT(0);
        }

        return ret;
}

static int __bh_task_start(bh_task_t *task)
{
        int ret;

        YASSERT(!task->inited);

#if 0
        return 0;
#endif        
        
        DINFO("%s file service start, interval %ju\n", task->name, task->interval);

        ret = timer1_create(&task->worker_handler, task->name, __bh_task_worker, task);
        if (unlikely(ret)) {
                DERROR("ret %d\n", ret);
                GOTO(err_ret, ret);
        }

        ret = timer1_settime(&task->worker_handler, task->interval);
        if (unlikely(ret)) {
                DERROR("ret %d\n", ret);
                GOTO(err_ret, ret);
        }

        DINFO("%s file service started, interval %ju\n", task->name, task->interval);
        task->inited = 1;

        return 0;
err_ret:
        return ret;
}

// ------------------------------------------------------------------------
// ------------------------------------------------------------------------

bh_task_t volume_cleanup_bh_task;
bh_task_t rollback_bh_task;
bh_task_t flat_bh_task;

/**
 * 由admin节点负责扫描相关目录，并向各个控制器节点下发任务。
 * 每个控制器节点上运行有volume_bh工作队列，由节点上的独立线程进行任务处理
 *
 */
int rmvol_bh_start()
{
        int ret;

        bh_task_t *task = &volume_cleanup_bh_task;

        strcpy(task->name, "volume_bh");
        strcpy(task->root, UNLINK_ROOT);
        task->interval = USEC_PER_MIN;
        task->func = volume_cleanup_bh;

        ret = __bh_task_start(task);
        if (unlikely(ret)) {
                DERROR("rmvol_bh_start fail!!! ret:%d\n", ret);
                YASSERT(0);
        }

        return 0;
}

static void __rmvol_bh_stop()
{
        volume_cleanup_bh_task.inited = 0;
}

static void __flat_bh_stop()
{
        flat_bh_task.inited = 0;
}

static void __rollback_bh_stop()
{
        rollback_bh_task.inited = 0;
}


int rollback_bh_start()
{
        int ret;

        bh_task_t *task = &rollback_bh_task;

        strcpy(task->name, "rollback_bh");
        strcpy(task->root, ROLLBACK_ROOT);
        task->interval = USEC_PER_MIN;
        task->func = volume_rollback_bh;

        ret = __bh_task_start(task);
        if (unlikely(ret)) {
                DERROR("rollback_bh_start fail!!! ret:%d\n", ret);
                YASSERT(0);
        }

        return 0;
}

int flat_bh_start()
{
        int ret;
        bh_task_t *task = &flat_bh_task;

        strcpy(task->name, "flat_bh");
        strcpy(task->root, FLAT_ROOT);
        task->interval = USEC_PER_MIN;
        task->func = volume_flat_bh;

        ret = __bh_task_start(task);
        if (unlikely(ret)) {
                DERROR("flat_bh_start fail!!! ret:%d\n", ret);
                YASSERT(0);
        }

        return 0;
}

int bh_task_start()
{
        rmvol_bh_start();
        rollback_bh_start();
        flat_bh_start();
        rmsnap_bh_start();
        etcd_task_start();
        
        return 0;
}


void bh_task_stop()
{
        __rmvol_bh_stop();
        __rollback_bh_stop();
        __flat_bh_stop();
        rmsnap_bh_stop();
        etcd_task_stop();
}
